type Interceptors = 'onRequestSuccess' | 'onResponseSuccess' | 'onResponseFail'

export interface RequestInterceptorsManagerPlugin<T, K, V> {
  name: string
  zIndex?: number
  onRequestSuccess?: (config: T) => void | Promise<any> | T
  onResponseSuccess?: (config: K) => void | Promise<any> | T
  onResponseFail?: (config: V) => void | Promise<any> | T
  onResponseFinally?: (config: T | V) => T | V | void
  disabled?: boolean
}

export default class RequestInterceptorsManager<RequestConfig, ResponseSuccessConfig, ResponseFailConfig> {
  private _plugins: any[] = []

  constructor() { }

  _executePlugin<T extends Interceptors>(type: 'onRequestSuccess', config: RequestConfig): void | Promise<any>
  _executePlugin<T extends Interceptors>(type: 'onResponseSuccess', config: ResponseSuccessConfig): void | Promise<any>
  _executePlugin<T extends Interceptors>(type: 'onResponseFail', config: ResponseFailConfig): void | Promise<any>

  async _executePlugin(type: 'onRequestSuccess' | 'onResponseSuccess' | 'onResponseFail', config: any) {
    if (this._plugins.length) {
      for (let i = 0; i < this._plugins.length; i++) {
        const task = this._plugins[i]
        const plugin = task[type]
        if (plugin) {
          const res = plugin(config)
          if (!res) break
          if (isPromise(res)) {
            try {
              config = await res
            }
            catch (e: any) {
              config = Promise.reject(e)
              break
            }
          }
          else {
            config = res
          }
        }
        if (task.onResponseFinally) {
          task.onResponseFinally(config)
        }
      }
    }
    return config
  }

  registerRequestSuccess(config: RequestConfig): void | Promise<any> {
    return this._executePlugin('onRequestSuccess', config)
  }

  registerResponseSuccess(config: ResponseSuccessConfig): void | Promise<any> {
    return this._executePlugin('onResponseSuccess', config)
  }

  registerResponseFail(config: ResponseFailConfig): void | Promise<any> {
    return this._executePlugin('onResponseFail', config)
  }

  addPlugin(plugin: RequestInterceptorsManagerPlugin<RequestConfig, ResponseSuccessConfig, ResponseFailConfig>) {
    if (!plugin) throw new Error('plugin is undefined')
    if (typeof plugin !== 'object') throw new Error('plugin must be object')
    if (this._plugins.find(p => p.name === plugin.name)) throw new Error(`plugin ${plugin.name} has register`)
    if (plugin.disabled) return this

    this._plugins.push(plugin)
    this._plugins = this._plugins.sort((a, b) => (a.zIndex || 0) - (b.zIndex || 0))

    return this
  }

  addPlugins(plugins: RequestInterceptorsManagerPlugin<RequestConfig, ResponseSuccessConfig, ResponseFailConfig>[]) {
    this._plugins = this._plugins.concat(plugins.filter(e => !e.disabled))
    this._plugins.sort((a, b) => (a.zIndex || 0) - (b.zIndex || 0))

    return this
  }
}

function isPromise(obj: any): boolean {
  return !!obj && typeof obj.then === 'function'
}
